🎖️GitЯра🎖️
feature/firmware/README.md 4d4070c8e1ed73a56ee2f054300a0f62e3fe2e62 (4d4070c8) Text, 9.23 KB
T383838:feature:firmware
Firmware Update System
The T383838:feature:firmware module provides a unified interface for updating Meshtastic devices across different platforms and connection types.
Supported Platforms & Methods
Meshtastic-Android supports three primary firmware update flows:
1. ESP32 Unified OTA (WiFi & BLE)
Used for modern ESP32 devices (e.g., Heltec V3, T-Beam S3). This method utilizes the Unified OTA Protocol, which enables high-speed transfers over TCP (port 3232) or BLE. The BLE transport uses the Kable multiplatform library for architectural consistency and modern coroutine support.
Key Features:
• Pre-shared Hash Verification: The app sends the firmware SHA256 hash in an initial T383838AdminMessage trigger. The device stores this in NVS and verifies the incoming stream against it.
• Connection Retry: Robust logic to wait for the device to reboot and start the OTA listener.
• Automatic MTU Handling & Fragmentation: The BLE transport automatically detects the negotiated MTU and fragments data chunks into packets that fit. It carefully manages acknowledgments for each fragmented packet to ensure reliability even on congested connections.
T282828
Te6edf3sequenceDiagram
Te6edf3participant Te6edf3App Tff7b72as Te6edf3Android Te6edf3App
Te6edf3participant Te6edf3Radio Tff7b72as Te6edf3Mesh Te6edf3Node Tb4b4b4(Te6edf3AdminTb4b4b4)
Te6edf3participant Te6edf3OTA Tff7b72as Te6edf3ESP32 Te6edf3OTA Te6edf3Mode
Te6edf3Note Te6edf3over Te6edf3AppTff7b72: Te6edf3Phase T79c0ff1Tff7b72: Te6edf3Preparation
Te6edf3AppTff7b72->>Te6edf3AppTff7b72: Te6edf3Calculate Te6edf3SHA256 Td2a8ffHash
Te6edf3Note Te6edf3over Te6edf3AppTb4b4b4, Te6edf3RadioTff7b72: Te6edf3Phase T79c0ff2Tff7b72: Te6edf3Trigger Te6edf3Reboot
Te6edf3AppTff7b72->>Te6edf3RadioTff7b72: Te6edf3AdminMessage Tb4b4b4(Te6edf3ota_request Tff7b72= Te6edf3mode Tff7b72+ Td2a8ffhashTb4b4b4)
Te6edf3RadioTff7b72->>Te6edf3RadioTff7b72: Tff7b72Store Td2a8ffHash Tff7b72in Te6edf3NVS Tff7b72& Te6edf3Reboot
Te6edf3Note Te6edf3over Te6edf3AppTb4b4b4, Te6edf3OTATff7b72: Te6edf3Phase T79c0ff3Tff7b72: Te6edf3Connection Tff7b72& Te6edf3Update
Te6edf3AppTff7b72->>Te6edf3OTATff7b72: Te6edf3Connect Tb4b4b4(Te6edf3TCPTff7b72:T79c0ff3232 Tff7b72or Te6edf3BLETb4b4b4)
Te6edf3AppTff7b72->>Te6edf3OTATff7b72: Te6edf3Handshake Tff7b72& Te6edf3Version Te6edf3Check
Te6edf3AppTff7b72->>Te6edf3OTATff7b72: Te6edf3Start Te6edf3OTA Tb4b4b4(Te6edf3Size Tff7b72+ Td2a8ffHashTb4b4b4)
Td2a8ffloop Te6edf3Streaming
Te6edf3AppTff7b72->>Te6edf3OTATff7b72: Te6edf3Stream Tffa657Data Te6edf3Chunks
Te6edf3OTATff7b72-->>Te6edf3AppTff7b72: Te6edf3ACK
Tff7b72end
Te6edf3AppTff7b72->>Te6edf3OTATff7b72: Te6edf3REBOOT Te6edf3Command
2. nRF52 BLE DFU
The standard update method for nRF52-based devices (e.g., RAK4631). Uses a pure KMP Nordic Secure DFU implementation built on Kable — no dependency on the Nordic DFU library. The protocol stack (T383838SecureDfuTransport, T383838SecureDfuProtocol, T383838SecureDfuHandler) handles DFU ZIP parsing, init packet validation, firmware streaming with CRC verification, and PRN-based flow control.
T282828
Te6edf3sequenceDiagram
Te6edf3participant Te6edf3App Tff7b72as Te6edf3Android Te6edf3App
Te6edf3participant Te6edf3Radio Tff7b72as Te6edf3Mesh Te6edf3Node
Te6edf3participant Te6edf3DFU Tff7b72as Te6edf3nRF Te6edf3DFU Te6edf3Bootloader
Te6edf3AppTff7b72-Tff7b72>>Te6edf3RadioTb4b4b4: Te6edf3Trigger Te6edf3DFU Te6edf3Mode
Te6edf3RadioTff7b72-Tff7b72>>Te6edf3RadioTb4b4b4: Te6edf3Reboot Te6edf3into Te6edf3Bootloader
Te6edf3AppTff7b72-Tff7b72>>Te6edf3DFUTb4b4b4: Te6edf3Connect Te6edf3via Te6edf3BLE
Te6edf3AppTff7b72-Tff7b72>>Te6edf3DFUTb4b4b4: Te6edf3Initialize Te6edf3DFU Te6edf3Transaction
Te6edf3loop Te6edf3Transfer
Te6edf3AppTff7b72-Tff7b72>>Te6edf3DFUTb4b4b4: Te6edf3Stream Te6edf3ZIP Te6edf3Segments
Te6edf3DFUTff7b72-Tff7b72-Tff7b72>>Te6edf3AppTb4b4b4: Te6edf3Progress
Te6edf3end
Te6edf3DFUTff7b72-Tff7b72>>Te6edf3DFUTb4b4b4: Te6edf3VerifyTb4b4b4, Te6edf3Swap Tff7b72& Te6edf3Reboot
3. USB / UF2 (RP2040, nRF52, STM32)
For devices supporting USB Mass Storage updates. The app triggers the device into its native bootloader mode, then guides the user to save the UF2 firmware file to the mounted drive.
T282828
Te6edf3sequenceDiagram
Te6edf3participant Te6edf3App Tff7b72as Te6edf3Android Te6edf3App
Te6edf3participant Te6edf3Radio Tff7b72as Te6edf3Mesh Te6edf3Node
Te6edf3participant Te6edf3USB Tff7b72as Te6edf3USB Te6edf3Mass Te6edf3Storage
Te6edf3AppTff7b72->>Te6edf3RadioTff7b72: Te6edf3rebootToDfuTb4b4b4(Tb4b4b4)
Te6edf3RadioTff7b72->>Te6edf3RadioTff7b72: Te6edf3Mounts Tff7b72as Te6edf3MESH_DRIVE
Te6edf3AppTff7b72->>Te6edf3AppTff7b72: Te6edf3Prompt Te6edf3User Te6edf3to Te6edf3Save Te6edf3UF2
Te6edf3AppTff7b72->>Te6edf3USBTff7b72: Te6edf3Write Te6edf3firmwareTb4b4b4.Te6edf3uf2
Te6edf3USBTff7b72->>Te6edf3USBTff7b72: Te6edf3AutoTff7b72-Te6edf3Flash Tff7b72& Te6edf3Reboot
4. USB Maintenance: Factory Erase & OTAFIX Bootloader Upgrade
An nRF52/RP2040 device can also run a factory erase (wipes the internal filesystem, useful for a device stuck in a bad state or carrying stale event-firmware state) or, on boards OTAFIX ships a bootloader for, a bootloader self-update. The erase is reached through the update screen's "Erase device during update" opt-in (default off) rather than a standalone action, so a wipe always ends with the selected release installed; over BLE/WiFi the same opt-in instead sends an admin factory reset once the update is verified. Both USB flows are two-pass sequences: the maintenance image (erase or OTAFIX) is written first, which reboots the device back into a bare bootloader; the release firmware is then written as the second, ordinary UF2 pass.
Two runtime facts make the maintenance image itself safety-critical, not just another UF2 write:
• The nRF52 erase image is SoftDevice-version-specific. Writing the S140 6.1.1 image to a 7.3.0 device (or vice versa) corrupts the SoftDevice with no on-device recovery. T383838MaintenanceUf2.kt treats the mounted volume's own T383838INFO_UF2.TXT T383838SoftDevice: line as authoritative over the bundled hardware-catalog hint — the two must agree, or the app refuses rather than guessing (T383838EraseImageResolution.Conflict).
• OTAFIX bootloaders are resolved by T383838Board-ID, not by build target or USB VID/PID — both of the latter collide across multiple boards. T383838otafixUf2ForBoardId() looks up the exact bootloader image for the T383838Board-ID: line the volume reports; the Meshtastic build-target name is only ever used to decide whether to offer the action in the UI.
T282828
Te6edf3sequenceDiagram
Te6edf3participant Te6edf3App Tff7b72as Te6edf3Android Te6edf3App
Te6edf3participant Te6edf3Radio Tff7b72as Te6edf3Mesh Te6edf3Node
Te6edf3participant Te6edf3USB Tff7b72as Te6edf3USB Te6edf3Mass Te6edf3Storage
Te6edf3AppTff7b72-Tff7b72>>Te6edf3RadioTb4b4b4: Te6edf3rebootToDfuTb4b4b4(Tb4b4b4)
Te6edf3RadioTff7b72-Tff7b72>>Te6edf3RadioTb4b4b4: Te6edf3Mounts Tff7b72as Te6edf3UF2 Te6edf3bootloader Te6edf3drive
Te6edf3AppTff7b72-Tff7b72>>Te6edf3USBTb4b4b4: Te6edf3Read Te6edf3INFO_UF2Tff7b72.Td2a8ffTXT Tb4b4b4(Te6edf3BoardTff7b72-Te6edf3IDTb4b4b4, Te6edf3SoftDeviceTb4b4b4)
Te6edf3AppTff7b72-Tff7b72>>Te6edf3AppTb4b4b4: Te6edf3Resolve Te6edf3eraseTff7b72/Te6edf3OTAFIX Te6edf3imageTb4b4b4, Te6edf3verify Te6edf3digest Tff7b72+ Te6edf3target Te6edf3address
Te6edf3AppTff7b72-Tff7b72>>Te6edf3USBTb4b4b4: Te6edf3Write Te6edf3maintenance Te6edf3image
Te6edf3USBTff7b72-Tff7b72>>Te6edf3USBTb4b4b4: Te6edf3AutoTff7b72-Te6edf3flash Tff7b72& Te6edf3reboot Te6edf3to Te6edf3bare Te6edf3bootloader
Te6edf3AppTff7b72-Tff7b72>>Te6edf3AppTb4b4b4: Te6edf3Prompt Te6edf3User Te6edf3to Te6edf3Save Te6edf3release Te6edf3firmware
Te6edf3AppTff7b72-Tff7b72>>Te6edf3USBTb4b4b4: Te6edf3Write Te6edf3firmwareTff7b72.Td2a8ffuf2 Tb4b4b4(Tff7b72pass T79c0ff2Tb4b4b4)
Te6edf3USBTff7b72-Tff7b72>>Te6edf3USBTb4b4b4: Te6edf3AutoTff7b72-Te6edf3Flash Tff7b72& Te6edf3Reboot
A T383838FirmwareMaintenanceLock (T383838:core:common) is held for the duration of the sequence so T383838SharedRadioInterfaceService's environmental-recovery listeners don't claim the erase firmware's bare CDC port out from under the flow; it is released when the sequence's terminal pass completes, fails, or the ViewModel is cleared mid-sequence.
Key Classes
• T383838FirmwareUpdateManager.kt: Top-level orchestrator for all firmware update flows.
• T383838FirmwareUpdateViewModel.kt: UI state management (MVI pattern) for the firmware update screen.
• T383838FirmwareRetriever.kt: Handles downloading and extracting firmware assets (ZIP/BIN/UF2) with manifest-based ESP32 resolution.
• T383838Esp32OtaUpdateHandler.kt: Orchestrates the Unified OTA flow for ESP32 devices.
• T383838WifiOtaTransport.kt: Implements the TCP transport logic for ESP32 OTA.
• T383838BleOtaTransport.kt: Implements the BLE transport logic for ESP32 OTA using Kable.
• T383838UnifiedOtaProtocol.kt: Shared OTA protocol framing (handshake, streaming, acknowledgment).
• T383838SecureDfuHandler.kt: Orchestrates the nRF52 Secure DFU flow (bootloader entry, DFU ZIP parsing, firmware transfer).
• T383838SecureDfuProtocol.kt: Low-level Nordic Secure DFU protocol operations (init packet, data transfer, CRC verification).
• T383838SecureDfuTransport.kt: BLE transport layer for Secure DFU using Kable (control/data point characteristics, PRN flow control).
• T383838DfuZipParser.kt: Parses Nordic DFU ZIP archives (manifest, init packet, firmware binary).
• T383838UsbUpdateHandler.kt: Handles USB/UF2 firmware updates across platforms.
• T383838MaintenanceUf2.kt: Pinned erase/OTAFIX image tables, T383838INFO_UF2.TXT parsing (Board-ID, SoftDevice), and the drive-vs-map SoftDevice resolution used to pick a safe erase image.
• T383838UsbMaintenance.kt: Pure gating (T383838usbMaintenanceGate) and volume-inspection/image-choice types for the factory-erase and bootloader-upgrade actions.
• T383838UsbUpdateSupport.kt: Sequences a maintenance pass (download → reboot to DFU → vet volume → write → confirm landed) and drives the two-pass state machine.
Dependency Graph
<!--region graph-->
T282828
Te6edf3graph Te6edf3TB
:Te6edf3featureTb4b4b4:Te6edf3firmwareTff7b72[Te6edf3firmwareTff7b72]Tff7b72:::Te6edf3kmpTff7b72-Te6edf3feature
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3ble
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3common
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Tff7b72data
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Tff7b72database
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3datastore
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3di
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3model
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3navigation
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3network
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3prefs
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3repository
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3service
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3resources
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3ui
:Te6edf3featureTb4b4b4:Te6edf3firmware Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3testing
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3application Te6edf3fillTb4b4b4:Te6edf3#CAFFBFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3applicationTff7b72-Te6edf3compose Te6edf3fillTb4b4b4:Te6edf3#CAFFBFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3composeTff7b72-Te6edf3desktopTff7b72-Te6edf3application Te6edf3fillTb4b4b4:Te6edf3#CAFFBFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3feature Te6edf3fillTb4b4b4:Te6edf3#FFD6A5Tb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3library Te6edf3fillTb4b4b4:Te6edf3#9BF6FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3libraryTff7b72-Te6edf3compose Te6edf3fillTb4b4b4:Te6edf3#9BF6FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3test Te6edf3fillTb4b4b4:Te6edf3#A0C4FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3jvmTff7b72-Te6edf3library Te6edf3fillTb4b4b4:Te6edf3#BDB2FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3kmpTff7b72-Te6edf3feature Te6edf3fillTb4b4b4:Te6edf3#FFD6A5Tb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3kmpTff7b72-Te6edf3libraryTff7b72-Te6edf3compose Te6edf3fillTb4b4b4:Te6edf3#FFC1CCTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3kmpTff7b72-Te6edf3library Te6edf3fillTb4b4b4:Te6edf3#FFC1CCTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Tff7b72unknown Te6edf3fillTb4b4b4:Te6edf3#FFADADTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
<!--endregion-->
Served by rngit 1.5.0 - Generated in 0.17s